package com.tarena.lbs.coupon.rpc;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.coupon.api.CouponApi;
import com.tarena.lbs.coupon.api.pojo.param.UserCouponsParam;
import com.tarena.lbs.coupon.dao.mapper.StockMapper;
import com.tarena.lbs.coupon.pojo.po.CouponStockPO;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * @Author: goodman
 * @Date: 2025/5/15 17:31
 */

@Slf4j
@Component
@DubboService
public class CouponApiImpl implements CouponApi {

    @Autowired
    private StockMapper stockMapper;
    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public void receiveCoupon(UserCouponsParam param) throws BusinessException {
    }

    @Override
    public Integer getCouponStock(Integer couponId) {
        log.info("获取优惠券库存");
        log.info("优惠券ID:{}",couponId);
        QueryWrapper<CouponStockPO> queryWrapper = new QueryWrapper<CouponStockPO>();
        queryWrapper.eq("coupon_id", couponId);
        CouponStockPO couponStockPO = stockMapper.selectOne(queryWrapper);
        log.info("优惠券库存:{}", couponStockPO);
        Integer num = couponStockPO.getNum();
        log.info("优惠券库存数量:{}", num);
        return num;
    }

    @Override
    /**
     * 在CouponApi中实现reserveStock方法
     * 用于TCC事务的Try阶段，预留指定数量的优惠券库存
     */
    public Boolean reserveStock(Integer couponId, Integer quantity) {
        // 1. 参数校验
        if (couponId == null || quantity == null || quantity <= 0) {
            log.warn("参数不合法，couponId: {}, quantity: {}", couponId, quantity);
            return false;
        }

        // 2. 构造库存键和预留键
        String stockKey = "coupon:stock:" + couponId;           // 当前库存
        String reservedKey = "coupon:reserved:" + couponId;     // 已预留库存

        // 3. 使用Lua脚本保证原子性操作
        String luaScript =
                "local current_stock = redis.call('GET', KEYS[1]) " +
                        "local reserved_stock = redis.call('GET', KEYS[2]) " +
                        "if current_stock == false then " +
                        "   return -1 " +  // 库存键不存在
                        "end " +
                        "if reserved_stock == false then " +
                        "   reserved_stock = 0 " +
                        "end " +
                        "current_stock = tonumber(current_stock) " +
                        "reserved_stock = tonumber(reserved_stock) " +
                        "local request_quantity = tonumber(ARGV[1]) " +
                        "if current_stock >= request_quantity then " +
                        "   redis.call('DECRBY', KEYS[1], request_quantity) " +  // 扣减当前库存
                        "   redis.call('INCRBY', KEYS[2], request_quantity) " +   // 增加预留库存
                        "   return 1 " +  // 成功
                        "else " +
                        "   return 0 " +  // 库存不足
                        "end";

        try {
            List<String> keys = Arrays.asList(stockKey, reservedKey);
            Long result = (Long) redisTemplate.execute(
                    new DefaultRedisScript<>(luaScript, Long.class),
                    keys,
                    String.valueOf(quantity)
            );

            switch (result.intValue()) {
                case 1:  // 成功预留库存
                    log.info("成功预留优惠券库存，couponId: {}, quantity: {}", couponId, quantity);
                    return true;
                case 0:  // 库存不足
                    log.warn("优惠券库存不足，couponId: {}, requested: {}, available: unknown", couponId, quantity);
                    return false;
                case -1: // 库存键不存在
                    log.error("优惠券库存键不存在，couponId: {}", couponId);
                    return false;
                default:
                    log.error("未知错误，couponId: {}, result: {}", couponId, result);
                    return false;
            }
        } catch (Exception e) {
            log.error("预留优惠券库存异常，couponId: {}, quantity: {}", couponId, quantity, e);
            return false;
        }
    }

    @Override
    public Boolean reduceStock(Integer couponId, Integer quantity) {
        log.info("优惠券ID:{}",couponId);
        log.info("扣减优惠券库存");
        QueryWrapper queryWrapper = new QueryWrapper<CouponStockPO>();
        queryWrapper.eq("coupon_id", couponId);
        CouponStockPO couponStockPO = stockMapper.selectOne(queryWrapper);
        log.info("优惠券库存:{}", couponStockPO);
        Integer num = couponStockPO.getNum();
        if (num < quantity) {
            log.warn("库存不足，couponId: {}, requested: {}, available: {}", couponId, quantity, num);
            return false;
        }
        couponStockPO.setNum(num - quantity);
        log.info("检查");
        stockMapper.updateById(couponStockPO);
        log.info("检查2");
        return true;
    }

    @Override
    // 在CouponApiImpl类中实现
    public Boolean releaseStock(Integer couponId, Integer quantity) throws BusinessException {
        // 1. 参数校验
        if (couponId == null || quantity == null || quantity <= 0) {
            return false;
        }

        // 2. 构造Redis键
        String stockKey = "coupon:stock:" + couponId;       // 当前库存
        String reservedKey = "coupon:reserved:" + couponId; // 预留库存

        // 3. 使用Lua脚本原子性地释放库存
        String luaScript =
                "local reserved_stock = redis.call('GET', KEYS[2]) " +
                        "if reserved_stock == false then " +
                        "   reserved_stock = 0 " +
                        "end " +
                        "reserved_stock = tonumber(reserved_stock) " +
                        "local request_quantity = tonumber(ARGV[1]) " +
                        "if reserved_stock >= request_quantity then " +
                        "   redis.call('INCRBY', KEYS[1], request_quantity) " +   // 增加当前库存
                        "   redis.call('DECRBY', KEYS[2], request_quantity) " +   // 减少预留库存
                        "   return 1 " +  // 成功
                        "else " +
                        "   return 0 " +  // 预留库存不足
                        "end";

        // 4. 执行脚本并返回结果
        try {
            List<String> keys = Arrays.asList(stockKey, reservedKey);
            Long result = (Long) redisTemplate.execute(
                    new DefaultRedisScript<>(luaScript, Long.class),
                    keys,
                    String.valueOf(quantity)
            );
            return result == 1;
        } catch (Exception e) {
            log.error("释放优惠券库存异常，couponId: {}, quantity: {}", couponId, quantity, e);
            return false;
        }
    }



}
